#define _BSD_SOURCE
#define NDEBUG
#include "Cinta.h"
#include "hash.h"
#include "error.h"
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define TAM_HASH 1021

struct sHash
{
  ListaAsoc arr[TAM_HASH];
  int cantelem;
};

Hash
hash_empty (void)
{
	Hash h=NULL;
	int i=0;

	h = (Hash) calloc (1,sizeof(struct sHash));

	/* INV: (h->arr).j=NULL pt j<i, 0<=i<TAM_HASH */
	for(;i<TAM_HASH;i++)
		(h -> arr)[i] = la_empty();

	h -> cantelem = 0;

	return h;
}

void
hash_add (Hash h, Key k, Data d)
{
	if(h==NULL)
		errx(EXIT_FAILURE,ERRFMT,"hash_add: hash null",__LINE__);

	if (k==NULL || d==NULL)
		errx(EXIT_FAILURE,ERRFMT,"hash_add: Key or Data null",__LINE__);

	la_add((h->arr)[f_hash(k)],k,d);
	
	h->cantelem=h -> cantelem+1;

}

Bool
hash_exists (Hash h, Key k)
{
	Bool b = !(h==NULL || k==NULL);

	if(b)
		b = la_exists(h->arr[f_hash(k)],k);

	return b;
}

Data
hash_search (Hash h, Key k)
{
	return la_search(h->arr[f_hash(k)],k);
}

void
hash_del (Hash h, Key k)
{
	la_del(h->arr[f_hash(k)],k);
}

int
hash_length (Hash h)
{
	if(h == NULL)
	{
		warnx(ERRFMT,"hash_length: Hash nulo",__LINE__);
		return -1;
	}
	else
	{
		return h -> cantelem;
	}
}

Hash
hash_fromFile(char *nomfile)
{
	Key k=NULL;
	Data d=NULL;
	Hash h=hash_empty();
	cr c_disco = NULL;
	
	if(h == NULL)
	{
		warnx(ERRFMT,"hash_fromFile: lack of memory",__LINE__);
		return h;
	}

	c_disco = cr_create(nomfile);

	if(c_disco==NULL)
		warn(ERRFMT,"hash_fromFile",__LINE__);
	else
	{
		cr_arr(c_disco);
		while(!cr_fin(c_disco))
		{
			k=tuple_fst(cr_elec(c_disco));
			d=tuple_snd(cr_elec(c_disco));
			hash_add(h,k,d);
			k = key_destroy(k);
			d = data_destroy(d);
			cr_av(c_disco);
		}
		c_disco = cr_destroy(c_disco);
	}
#ifndef NDEBUG
	printf (" Esta es la MEDIA =====> %f\n", media(h));
	printf(" Esta es la VARIANZA =====> %f\n", varianza(h));
#endif
	return h;
}

void
hash_toFile(char *nomfile, Hash h)
{
	int i=0;
	cw c_disco = cw_create(nomfile);
	
      cw_arr(c_disco);
	
	for(;i<TAM_HASH;i++)
	{
		if(la_length((h -> arr)[i]) > 0) 
			la_toCinta(c_disco,(h -> arr)[i]);
	}
	c_disco=cw_destroy(c_disco);
}

Hash
hash_destroy(Hash h)
{
      int i=0;
      if(h == NULL)
            return NULL;
      else
      {
            for(;i<TAM_HASH;i++)
            {
                  (h -> arr)[i]=la_destroy((h -> arr)[i]);
            }
            free(h);
            h=NULL;
            return h;
      }
}

int
f_hash (Key k)
{
	char *str;
	int i=0,l=0,res=0;
	l = key_length(k);
	str=(char*)calloc(l+1,sizeof(char));
	key_toString(k,str);
	while(i < l)
	{
		res = (res + toascii((int)str[i]))%TAM_HASH;
		i=i+1;
	}

	res = res;
	free(str);
	str = NULL;
    return res;
}

/*	VERSIÓN RARA
int
f_hash (Key k)
{
	int pos;
	char *str;
	char *c;
	str=(char*)calloc(key_length(k)+1,sizeof(char));
	key_toString(k,str);

	pos=0;
	for( c = str ;*c ; c++)
		pos+= (int) (*c);
	free (str);
	str = NULL;
	return (pos % TAM_HASH);

}
*/
void hash_pprint(Hash h)
{
      int i=0;
      if(h == NULL)
      {
            err(EXIT_FAILURE,ERRFMT,"hash_pprint: hash nulo",__LINE__);
      }
      for(;i<TAM_HASH;i++)
      {
            if(la_length((h -> arr)[i]) > 0)
                  la_pprint((h -> arr)[i]);
      }
}

float
media (Hash h)
{
	int i;
	float m=0.0;

	for (i=0;i<TAM_HASH;i++)
		m = m + la_length(h->arr[i]);

	return (m/((float)TAM_HASH));
}


/* La funcion "varianza de x" es la siguiente:

	var(x) = [ 1/N * suma(xi^2) ] - [ 1/N * suma(xi) ]^2

 donde xi=la_length(h->arr[i]) , suma es la sumatoria desde 
 1 hasta N , y N=TAM_HASH.
 Notar que "valor medio de x" = media(x) = 1/N * suma(xi)
 Entonces puede reescribirse la función de la varianza:

	var(x) = [ 1/N * suma(xi^2) ] - [ media(x) ]^2
*/
float
varianza (Hash h)
{
	int i=0;
	float N,xi,p=0.0;

	N = (float)TAM_HASH;

	for (; i<TAM_HASH ; i++)
	{
		xi = la_length(h->arr[i]);
		xi = xi*xi;
		p = p + xi;
	}

	return ( (1/N * p) - (media(h) * media(h)) );
}
